import { Node, v3, Vec3 } from 'cc';
import { NodeUtil } from 'db://app-game/core/utils/NodeUtil';
import { Vec3Util } from 'db://app-game/core/utils/Vec3Util';
import { ecs } from 'db://app-game/libs/ecs/ECS';
import { ECSEntity } from 'db://app-game/libs/ecs/ECSEntity';
import { GridNode } from 'db://app-game/libs/math/graph/Graph';
import { AStar } from 'db://app-game/libs/math/pathfinding/a-star/AStar';
import FlowGrid from 'db://app-game/libs/math/pathfinding/flow-field/FlowGrid';
import RVOMath from 'db://app-game/libs/math/rov2/RVOMath';
import Simulator from 'db://app-game/libs/math/rov2/Simulator';
import Vector2D from 'db://app-game/libs/math/rov2/Vector2D';
import { Lineup } from '../../biz/level-map/LevelMap.type';
import { smc } from '../../singleton/SingletonModuleComp';

/** 移动模式 */
export enum MoveModel {
  /** 流动网格移动 */
  FlowGridMove = 0,
  /** A星寻路移动  */
  AStarMove = 1,
}

/** 移动组件模组 */
@ecs.register('MoveRovToModel')
export class MoveRovToModelComp extends ecs.Comp {
  id: number = -1;
  /** 移动节点 */
  node: Node;
  /** 移动的地图节点 */
  map: Node;
  /** 移动方向 */
  velocity: Vec3 = new Vec3(0, 0, 0);
  /** 移动速度 (每秒移动的像素距离)*/
  speedOfMove: number = 0;
  /** 偏移距离 */
  offset: number = 0;
  /** 目标实体ECS编号、目标位置 */
  target: Vec3 | Node | null = null;
  /** 模拟器 */
  simulator: Simulator;
  /** 移动模式 */
  moveModel: MoveModel = MoveModel.FlowGridMove;
  /** 流动网格名称 */
  flowGridName: Lineup = Lineup.Player;
  /** 流动网格 */
  get flowGrid(): FlowGrid {
    return smc.level.LevelMap.LevelMapModelRender.flowGridObj[this.flowGridName];
  }
  /** 移动完成回调 */
  onComplete: Function | null = null;

  reset(entity: ecs.Entity): void {
    this.id = -1;
    this.node = null!;
    this.speedOfMove = 0;
    this.offset = 0;
  }
}

/** 变量移动到组件 */
@ecs.register('VariableMoveTo')
export class VariableMoveToComponent extends ecs.Comp {
  id: number = -1;
  /** 终点备份 */
  end: Vec3;
  /** 目标位置 */
  target!: Vec3;

  /** 如果是A星巡逻 */
  pathIndex: number = 0;
  /** 路径 */
  path: GridNode[] = [];
  reset(entity: ecs.Entity): void {
    this.id = -1;
    this.end = null;
    this.target = null;

    this.pathIndex = 0;
    this.path = [];
  }
}

/** 移动避障系统 */
@ecs.register('MoveRovTo')
export class MoveRovToSystem
  extends ecs.ComblockSystem
  implements ecs.IEntityEnterSystem, ecs.ISystemUpdate
{
  filter(): ecs.IMatcher {
    return ecs.allOf(MoveRovToModelComp);
  }
  entityEnter(entity: ECSEntity): void {
    // 1.获取组件信息
    const move = entity.get(MoveRovToModelComp);
    const mtv = entity.get(VariableMoveToComponent) || entity.add(VariableMoveToComponent);

    // 2.判断节点是否已经添加到避障模拟器中
    let id = mtv.id;
    if (id === -1) {
      id = move.simulator.addAgent(null);
      mtv.id = id;
    }
    const pos = NodeUtil.posConvert(v3(0, 0, 0), move.node, move.map);
    move.simulator.setAgentPosition(id, pos.x, pos.y);
    move.id = id;
  }

  update(entity: ECSEntity, dt: number): void {
    const move = entity.get(MoveRovToModelComp);
    const mtv = entity.get(VariableMoveToComponent);
    const graph = smc.level.LevelMap.LevelMapModelRender.graph;
    let startPos = NodeUtil.posConvert(v3(0, 0, 0), move.node, move.map);
    let end: Vec3;

    if (move.target instanceof Node) {
      end = NodeUtil.posConvert(v3(0, 0, 0), move.target, move.map);
    } else {
      end = move.target;
    }

    // 1.判断目标位置变更触发，重新计算移动方向与移动到幕布点的速度
    if (mtv.end === null || !mtv.end.strictEquals(end)) {
      let target = end.clone();

      // 设置移动方向
      move.velocity = Vec3Util.sub(target, startPos).normalize();

      // 离终点距离
      let distance = Vec3.distance(startPos, target) - move.offset;

      if (distance - move.offset <= 0) {
        this.exit(entity);
      } else {
        if (move.moveModel === MoveModel.FlowGridMove) {
        } else if (move.moveModel === MoveModel.AStarMove) {
          const aStar = new AStar(true);
          const startNode = graph.getNodeByPos(startPos);
          const endNode = graph.getNodeByPos(end);
          mtv.path = aStar.moveToward(graph, startNode, endNode);
          mtv.pathIndex = 0;
        }
        mtv.end = end.clone();
        mtv.target = move.velocity.clone().multiplyScalar(distance).add(startPos);
      }
    }

    // 2. 判断是否到达终点
    let endRov = new Vector2D(mtv.end.x, mtv.end.y);
    let startRov = new Vector2D(startPos.x, startPos.y);
    if (RVOMath.absSq(endRov.minus(startRov)) <= move.offset) {
      this.exit(entity);
    }

    // 3.节点移动
    if (move.speedOfMove > 0) {
      let trans: Vec3;
      let stepPos: Vec3;
      // 3.1 判断移动模式
      if (move.moveModel === MoveModel.FlowGridMove) {
        const flowNode = move.flowGrid.getVectorAt(startPos);
        if (!flowNode) return;
        // 1.获取方向向量
        move.velocity = new Vec3(flowNode.velocityX, -flowNode.velocityY).normalize();
      } else if (move.moveModel === MoveModel.AStarMove) {
        // 1.获取A星最近步骤位置
        stepPos = graph.gridNodeConvert(mtv.path[mtv.pathIndex]);
        // 2.计算移动方向
        move.velocity = Vec3Util.sub(stepPos, startPos).normalize();
      }

      trans = Vec3Util.mul(move.velocity, move.speedOfMove * this.dt);
      // 2.设置移动目标
      move.simulator.setAgentGoal(move.id, trans.x, trans.y);
      // 3.获取移动位置
      const agentPos = move.simulator.getAgentPosition(move.id);
      // 4.设置节点位置
      move.node.translate(v3(agentPos.x, agentPos.y), Node.NodeSpace.LOCAL);

      if (move.moveModel === MoveModel.AStarMove) {
        let startPos = NodeUtil.posConvert(v3(0, 0, 0), move.node, move.map);
        let distance = Vec3.distance(startPos, stepPos) - move.offset;
        if (distance - move.offset <= 0) {
          mtv.pathIndex++;
        }
      }
    }
  }

  private exit(e: ecs.Entity) {
    let move = e.get(MoveRovToModelComp);
    move.onComplete?.call(this);
    e.remove(VariableMoveToComponent);
    e.remove(MoveRovToModelComp);
  }
}
